package wasnake.grid;

import java.util.HashMap;

/**
 * Revised direction: provides an enumeration wrapper for degree directions for
 * legibility and ease of use in conditionals.
 * @author Elias Szabo-Wexler
 */

public enum Direction {
    /** 0 degrees, |*/
    NORTH (0),
    /** 45 degrees, / */
    NORTHEAST (45),
    /** 90 degrees, -*/
    EAST (90),
    /** 135 degrees, \*/
    SOUTHEAST (135),
    /** 180 degrees, |*/
    SOUTH (180),
    /** 225 degrees, /*/
    SOUTHWEST (225),
    /** 270 degrees, -*/
    WEST (270),
    /** 315 degrees, \*/
    NORTHWEST (315);

    /**
     * The map containing all possible directions for cross-referencing
     */
    protected static HashMap <Integer, Direction> map;
    /**
     * The individual degree measure for a given direction
     */
    protected final int degree;

    /**
     * Creates a direction associated with a degree, provided that direction
     * has not been initialized.
     * @param degree The angle of the direction [must be unique]
     * @throws IllegalArgumentException If the degree provided is already a
     * declared direction.
     */

    private Direction (int degree) {
        if (map (degree) != null)
            throw new IllegalArgumentException("Direction [" + degree +
                    "] already exists.");
        this.degree = degree;
        store(degree, this);
    }

    /**
     * Checks if this direction is a diagonal
     * @return true if NORTHEAST, SOUTHEAST, NORTHWEST, or SOUTHWEST
     */

    public boolean isDiagonal () {
        return this == NORTHEAST ||
                this == SOUTHEAST ||
                this == NORTHWEST ||
                this == SOUTHWEST;
    }
    
    /**
     * Checks if this direction is either vertical or horizontal
     * @return true if NORTH, SOUTH, EAST, or WEST
     */

    public boolean isStraight () {
        return !isDiagonal();
    }

    /**
     * Checks if this direction is vertical
     * @return true if NORTH or SOUTH
     */

    public boolean isVertical () {
        return this == NORTH || this == SOUTH;
    }
    
    /**
     * Checks if this direction is horizontal
     * @return true if EAST or WEST
     */

    public boolean isHorizontal () {
        return this == EAST || this == WEST;
    }

    /**
     * Gets the degree that this direction wraps
     * @return The integer degree value
     */

    public int degree () { return degree;}

    /**
     * Gets the direction that is opposite this one.
     * @return The diametrically opposite location.
     */

    public Direction opposite () {
        return map ((degree + 180) % 360);
    }

    /**
     * Stores a direction in the global map
     * @param deg The degree of the direction
     * @param d The associated direction
     */

    protected static void store (int deg, Direction d) {map.put(deg, d);}

    /**
     * Maps a numeric degree to its corrosponding direction.
     * @param degree The angle measure
     * @return The direction, or null if it doesn't exist
     */

    public static Direction map (int degree) {
        if (map == null) {
            map = new HashMap<Integer, Direction> ();
        }
        return map.get(degree);
    }

    /**
     * Utility method for grabbing the northwards directions.
     * @return An array containing NORTHWEST, NORTH, and NORTHEAST
     */

    public static Direction [] allNorth () {return new Direction [] {
        NORTHWEST, NORTH, NORTHEAST
    };}

    /**
     * Utility method for grabbing the northwards diagonal directions..
     * @return An array containing NORTHWEST and NORTHEAST
     */

    public static Direction [] ordinalNorth () {return new Direction [] {
        NORTHWEST, NORTHEAST
    };}

    /**
     * Utility method for grabbing the southwards directions.
     * @return An array containing SOUTHWEST, SOUTH, and SOUTHEAST
     */

    public static Direction [] allSouth () {return new Direction [] {
        SOUTHWEST, SOUTH, SOUTHEAST
    };}
    
    /**
     * Utility method for grabbing the southwards diagonal directions.
     * @return An array containing SOUTHWEST and SOUTHEAST
     */

    public static Direction [] ordinalSouth () {return new Direction [] {
        SOUTHWEST, SOUTHEAST
    };}
    
    /**
     * Utility method for grabbing the horizontal directions.
     * @return An array containing EAST and WEST
     */

    public static Direction [] horizontals () { return new Direction [] {
        EAST, WEST
    };}
    
    /**
     * Utility method for grabbing the vertical directions.
     * @return An array containing NORTH and SOUTH
     */

    public static Direction [] verticals () { return new Direction [] {
        NORTH, SOUTH
    };}

    /**
     * Utility method for grabbing the cardinal directions
     * @return An array containing NORTH, SOUTH, EAST, and WEST
     */

    public static Direction [] cardinals () { return new Direction [] {
        NORTH, SOUTH, EAST, WEST
    };}
    
    /**
     * Utility method for grabbing the ordinal directions
     * @return An array containing NORTHEAST, NORTHWEST, SOUTHEAST, and SOUTHWEST
     */

    public static Direction [] ordinals () { return new Direction [] {
        NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST
    };}

    /**
     * Utility method for grabbing the eastward ordinal directions
     * @return An array containing NORTHEAST and SOUTHEAST
     */

    public static Direction [] ordinalEast () {return new Direction [] {
        NORTHEAST, SOUTHEAST
    };}
    
    /**
     * Utility method for grabbing the eastward ordinal directions
     * @return An array containing NORTHWEST and SOUTHWEST
     */

    public static Direction [] ordinalWest () {return new Direction [] {
        NORTHWEST, SOUTHWEST
    };}
}